package com.justgame.project.cloud.common.query;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.justgame.project.cloud.base.entity.BaseEntity;
import com.justgame.project.cloud.base.entity.BaseTable;
import com.justgame.project.cloud.common.util.AppContextUtils;
import com.justgame.project.cloud.common.util.ArrayUtils;
import com.justgame.project.cloud.common.util.MyBatisUtils;
import lombok.Getter;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@SuppressWarnings("all")
public abstract class AbstractQueryWrapper<E extends BaseTable,W extends MPJLambdaWrapper<E>,DTO> {

    protected static final List<Query.QueryHandler> handlers;

    static {
        List<Query.QueryHandler> columnHandlers = new ArrayList<>(AppContextUtils.getBeans(Query.QueryHandler.class));
        columnHandlers.sort(Comparator.comparing(Query.QueryHandler::sort));
        handlers = columnHandlers;
    }
    /**
     * Wrapper
     */
    protected final W wrapper;

    /**
     * 关联查询数据表及字段
     */
    protected List<Query> queries;

    private Query masterQuery;

    @Getter
    private Page<?> page;

    protected AbstractQueryWrapper(W wrapper) {
        this.wrapper = wrapper;
    }

    public final <QW extends AbstractQueryWrapper<E,W,DTO>> QW select(Query... queries){
        Arrays.stream(queries).filter(query -> query.getCurrentTableClass().equals(wrapper.getEntityClass()))
                .findFirst().ifPresent(query -> masterQuery = query);
        this.queries = Arrays.stream(queries).peek(this::columnDispose).collect(Collectors.toList());
        return (QW)this;
    }

    public <QW extends AbstractQueryWrapper<E,W,DTO>> QW page(Page<?> page){
        this.page = page;
        return (QW) this;
    }

    public <QW extends AbstractQueryWrapper<E,W,DTO>> QW condition(Consumer<? super W> condition){
        condition.accept(wrapper);
        return (QW) this;
    }

    /**
     * Columns内部属性处理
     */
    private void columnDispose(Query query){
        query.setMaster(masterQuery);
        query.setMasterTableClass(wrapper.getEntityClass());
        for (Query.QueryHandler handler : handlers) {
            handler.handle(query);
        }
        if (ArrayUtils.notEmpty(query.getJoinQueries())){
            query.getJoinQueries().forEach(this::columnDispose);
        }
    }

    /**
     * 设置查询字段
     */
    public W getWrapper(){
        return wrapper;
    }

    public W build(){
        for (Query query : queries) {
            if (query.isMaster()){
                wrapper.select(query.getCurrentTableSelectColumns());
                continue;
            }
            joinWrapper(query);
        }
        return wrapper;
    }

    public void joinWrapper(Query query){
        wrapper.leftJoin(query.getCurrentTableClass(), MyBatisUtils.getAnnotationTableName(query.getCurrentTableClass()), query.getLeftOn(), query.getRightOn())
                .select(query.getCurrentTableSelectColumns());
        if (ArrayUtils.notEmpty(query.getJoinQueries())){
            query.getJoinQueries().forEach(this::joinWrapper);
        }
    }

    @SuppressWarnings("all")
    public <dto> Class<dto> getDtoClass(){
        Class<? extends AbstractQueryWrapper> thisClass = this.getClass();
        Type superClassType = thisClass.getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType)superClassType;
        Type[] genTypeArr = pt.getActualTypeArguments();
        Type genType = genTypeArr[2];
        if (false == genType instanceof Class){
            return null;
        }
        return (Class<dto>) genType;
    }

    public Optional<Query> findByTableClass(Class<?> clazz){
        return this.queries.stream().filter(column -> column.getCurrentTableClass().equals(clazz)).findFirst();
    }
}
